By: Kirti Kamerkar
Date:March 11 , 2025
Date:March 11 , 2025
Introduction to Computer Vision: Plant Seedlings Classification¶
Problem Statement¶
Context¶
In recent times, the field of agriculture has been in urgent need of modernizing, since the amount of manual work people need to put in to check if plants are growing correctly is still highly extensive. Despite several advances in agricultural technology, people working in the agricultural industry still need to have the ability to sort and recognize different plants and weeds, which takes a lot of time and effort in the long term. The potential is ripe for this trillion-dollar industry to be greatly impacted by technological innovations that cut down on the requirement for manual labor, and this is where Artificial Intelligence can actually benefit the workers in this field, as the time and energy required to identify plant seedlings will be greatly shortened by the use of AI and Deep Learning. The ability to do so far more efficiently and even more effectively than experienced manual labor, could lead to better crop yields, the freeing up of human inolvement for higher-order agricultural decision making, and in the long term will result in more sustainable environmental practices in agriculture as well.
Objective¶
The aim of this project is to Build a Convolutional Neural Netowrk to classify plant seedlings into their respective categories.
Data Dictionary¶
The Aarhus University Signal Processing group, in collaboration with the University of Southern Denmark, has recently released a dataset containing images of unique plants belonging to 12 different species.
The dataset can be download from Olympus.
The data file names are:
- images.npy
- Labels.csv
Due to the large volume of data, the images were converted to the images.npy file and the labels are also put into Labels.csv, so that you can work on the data/project seamlessly without having to worry about the high data volume.
The goal of the project is to create a classifier capable of determining a plant's species from an image.
List of Species
- Black-grass
- Charlock
- Cleavers
- Common Chickweed
- Common Wheat
- Fat Hen
- Loose Silky-bent
- Maize
- Scentless Mayweed
- Shepherds Purse
- Small-flowered Cranesbill
- Sugar beet
Note: Please use GPU runtime on Google Colab to execute the code faster.¶
Importing necessary libraries¶
Installing Required Libraries¶
# Installing the libraries with the specified version.
!pip install tensorflow scikit-learn seaborn matplotlib numpy pandas opencv-python -q --user
Note: After running the above cell, kindly restart the notebook kernel and run all cells sequentially from the start again.
# Restart kernel after installs so that your environment can access the new packages
import IPython
import time
app = IPython.Application.instance()
app.kernel.do_shutdown(True)
{'status': 'ok', 'restart': True}
Importing Required Libraries¶
import os
import numpy as np # Importing numpy for Matrix Operations
import pandas as pd # Importing pandas to read CSV files
import matplotlib.pyplot as plt # Importting matplotlib for Plotting and visualizing images
import math # Importing math module to perform mathematical operations
import cv2 # Importing openCV for image processing
import seaborn as sns # Importing seaborn to plot graphs
# Tensorflow modules
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator # Importing the ImageDataGenerator for data augmentation
from tensorflow.keras.models import Sequential # Importing the sequential module to define a sequential model
from tensorflow.keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D,BatchNormalization,SpatialDropout2D # Defining all the layers to build our CNN Model
from tensorflow.keras.optimizers import Adam,SGD # Importing the optimizers which can be used in our model
from sklearn import preprocessing # Importing the preprocessing module to preprocess the data
from sklearn.model_selection import train_test_split # Importing train_test_split function to split the data into train and test
from sklearn.metrics import confusion_matrix,accuracy_score # Importing confusion_matrix to plot the confusion matrix
from sklearn.preprocessing import LabelBinarizer
# Display images using OpenCV
from google.colab.patches import cv2_imshow # Importing cv2_imshow from google.patches to display images
from sklearn.model_selection import train_test_split
from tensorflow.keras import backend
from keras.callbacks import ReduceLROnPlateau
import random
# Ignore warnings
import warnings
warnings.filterwarnings('ignore')
Loading the dataset¶
# Connecting to google drive
from google.colab import drive
drive.mount('/content/drive')
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
# Load the image file of dataset
images = np.load('/content/drive/MyDrive/AI_LM_BusinessApplication/images.npy') # Complete the code to read the dataset
# Load the labels file of dataset
labels = pd.read_csv('/content/drive/MyDrive/AI_LM_BusinessApplication/Labels.csv') # Complete the code to read the dataset
from google.colab import drive
drive.mount('/content/drive')
Data Overview¶
Understand the shape of the dataset¶
#View the shape of image dataset
images.shape
(4750, 128, 128, 3)
#View the shape of label dataset
labels.shape
(4750, 1)
#checking first value from image dataset
images[0]
ndarray (128, 128, 3)
array([[[ 35, 52, 78],
[ 36, 49, 76],
[ 31, 45, 69],
...,
[ 78, 95, 114],
[ 76, 93, 110],
[ 80, 95, 109]],
[[ 33, 46, 68],
[ 37, 50, 73],
[ 48, 65, 83],
...,
[ 81, 96, 113],
[ 74, 89, 105],
[ 83, 95, 109]],
[[ 34, 50, 68],
[ 35, 52, 72],
[ 70, 85, 101],
...,
[ 83, 97, 112],
[ 79, 94, 108],
[ 79, 94, 107]],
...,
[[ 35, 50, 69],
[ 42, 57, 73],
[ 42, 57, 72],
...,
[ 60, 76, 92],
[ 67, 81, 97],
[ 64, 77, 95]],
[[ 36, 52, 67],
[ 48, 63, 78],
[ 41, 57, 73],
...,
[ 44, 66, 83],
[ 58, 76, 91],
[ 57, 74, 90]],
[[ 44, 58, 70],
[ 43, 57, 73],
[ 40, 55, 72],
...,
[ 41, 70, 92],
[ 55, 78, 97],
[ 61, 79, 96]]], dtype=uint8)#Viewing first 5 label values
labels.head()
| Label | |
|---|---|
| 0 | Small-flowered Cranesbill |
| 1 | Small-flowered Cranesbill |
| 2 | Small-flowered Cranesbill |
| 3 | Small-flowered Cranesbill |
| 4 | Small-flowered Cranesbill |
#Checking unique values from label dataset
labels.value_counts()
| count | |
|---|---|
| Label | |
| Loose Silky-bent | 654 |
| Common Chickweed | 611 |
| Scentless Mayweed | 516 |
| Small-flowered Cranesbill | 496 |
| Fat Hen | 475 |
| Charlock | 390 |
| Sugar beet | 385 |
| Cleavers | 287 |
| Black-grass | 263 |
| Shepherds Purse | 231 |
| Maize | 221 |
| Common wheat | 221 |
#labels information about its columns
labels.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 4750 entries, 0 to 4749 Data columns (total 1 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Label 4750 non-null object dtypes: object(1) memory usage: 37.2+ KB
# unique number of labels
labels.nunique()
| 0 | |
|---|---|
| Label | 12 |
Observations
- There are total 4750 images having width 128 and height 128in pixels with 3 channels.
- This indicates that the images are in color and have 3 channels (Red, Green, Blue).
- Labels dataset represents category of each seedlings.It has only one column "Label".There are total 12 unique categories.
Exploratory Data Analysis¶
- EDA is an important part of any project involving data.
- It is important to investigate and understand the data better before building a model with it.
- A few questions have been mentioned below which will help you understand the data better.
- A thorough analysis of the data, in addition to the questions mentioned below, should be done.
- How are these different category plant images different from each other?
- Is the dataset provided an imbalance? (Check with using bar plots)
Question1. How are these different category plant images different from each other?¶
categories=np.unique(labels)
categories
array(['Black-grass', 'Charlock', 'Cleavers', 'Common Chickweed',
'Common wheat', 'Fat Hen', 'Loose Silky-bent', 'Maize',
'Scentless Mayweed', 'Shepherds Purse',
'Small-flowered Cranesbill', 'Sugar beet'], dtype=object)
#Number of plant categories
len(categories)
12
#Defining function to display images with different 12 categories
def plot_images(images, labels):
"""
Displays a grid of images, one from each category, with their labels.
Args:
images: A NumPy array of images.
labels: A Pandas DataFrame containing the labels, with a column named 'Label'.
"""
# Get unique categories and create a dictionary mapping labels to category names
categories = np.unique(labels['Label'])
label_to_category = {i: category for i, category in enumerate(categories)}
# Number of rows and columns in the grid
rows = 5
cols = len(categories)
# Create the figure and axes
fig, axes = plt.subplots(rows, cols, figsize=(15,12)) # Adjust figsize as needed
# Iterate through categories and display a random image from each
for i, category in enumerate(categories):
# Filter labels for the current category
df_label = labels[labels['Label'] == category]
# Select a random image from this category
random_index = np.random.choice(df_label.index)
image = images[random_index]
# Display the image with its label
axes[0, i].imshow(image) # First row for the image
axes[0, i].set_title(category, rotation=45)
axes[0, i].axis('off')
# Display more images from the same category below
for j in range(1, rows):
random_index = np.random.choice(df_label.index)
image = images[random_index]
axes[j, i].imshow(image)
axes[j, i].axis('off')
plt.tight_layout()
plt.show()
#Plotting images with labels to check how different category images looks like
plot_images(images,labels)
Observations
- From the above plot we can see how each plant category is different from each other.
- There are 12 different kinds of plants and how images are classified.
Question2. Is the dataset provided an imbalance? (Check with using bar plots)¶
# Function to create labeled barplots
def labeled_barplot(data, feature, perc=False, n=None):
"""
Barplot with percentage at the top
data: dataframe
feature: dataframe column
perc: whether to display percentages instead of count (default is False)
n: displays the top n category levels (default is None, i.e., display all levels)
"""
total = len(data[feature]) # length of the column
count = data[feature].nunique()
if n is None:
plt.figure(figsize=(count + 1, 5))
else:
plt.figure(figsize=(n + 1, 5))
plt.xticks(rotation=90, fontsize=15)
ax = sns.countplot(
data=data,
x=feature,
palette="Paired",
order=data[feature].value_counts().index[:n].sort_values(),
)
for p in ax.patches:
if perc == True:
label = "{:.1f}%".format(
100 * p.get_height() / total
) # percentage of each class of the category
else:
label = p.get_height() # count of each level of the category
x = p.get_x() + p.get_width() / 2 # width of the plot
y = p.get_height() # height of the plot
ax.annotate(
label,
(x, y),
ha="center",
va="center",
size=12,
xytext=(0, 5),
textcoords="offset points",
) # annotate the percentage
plt.show() # show the plot
# Observing distribution of each traget label.
labeled_barplot(labels,'Label',perc=True)
Observations
- From the above bar plot we can say that distribution of each class is imbalanced.
- Loose silky bent has more samples of images i.e.13.8% as compared to common chickweed(12.9%)
- Common wheat and maize have the least representation in the dataset, each comprising only 4.7% of the images.
- Rest of the categories lies between 5.5% to 10.9%.
- The data above demonstrates an imbalance in the distribution of target variable data.
Data Pre-Processing¶
Convert the BGR images to RGB images.¶
- Converting images from BGR to RGB in this case is important to ensure proper visualization and compatibility with other libraries.
- -Without the conversion, the colors would be distorted, potentially affecting the analysis and understanding of the images.
# Converting the images from BGR to RGB using cvtColor function of OpenCV
for i in range(len(images)):
images[i] = cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB) # Complete the code to convert the images from BGR to RGB
#Images after converting from BGR To RGB
plot_images(images, labels)
Resize the images¶
As the size of the images is large, it may be computationally expensive to train on these larger images; therefore, it is preferable to reduce the image size from 128 to 64.
images_decreased=[]
height = 64 # defining the height as 64
width = 64 # defining the width as 64
dimensions = (width, height)
for i in range(len(images)):
images_decreased.append( cv2.resize(images[i], dimensions, interpolation=cv2.INTER_LINEAR))
#Observing the difference between original resized images
count = 2
Titles = ["Original", "Resized"]
image = [images[52], images_decreased[52]]
for i in range(count):
plt.subplot(1, count, i + 1)
plt.title(Titles[i])
plt.imshow(image[i])
plt.show();
Observations
- Here you can observe the difference between original and resized image which captures all the features as of original image.
- I've successfully decreased the size of the images while preserving their essential characteristics.
- This reduction in size can lead to faster model training and possibly improved performance by reducing the complexity of the data.
Data Preparation for Modeling¶
- Before you proceed to build a model, you need to split the data into train, test, and validation to be able to evaluate the model that you build on the train data
- You'll have to encode categorical features and scale the pixel values.
- You will build a model using the train data and then check its performance
Split the dataset
# split the data into train, test, and validation to be able to evaluate the model that you build on the train data
X_temp, X_test, y_temp, y_test = train_test_split(np.array(images_decreased), labels, test_size=0.1, random_state=42,stratify=labels)
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.1, random_state=42,stratify=y_temp)
X_train.shape,X_val.shape,X_test.shape
((3847, 64, 64, 3), (428, 64, 64, 3), (475, 64, 64, 3))
# Complete the code to check the shape of train, validation and test data
print("The shape of Training data:",(X_train.shape,y_train.shape))
print("The shape of Validation data:", (X_val.shape,y_val.shape))
print("The shape of Testing data:", (X_test.shape,y_test.shape))
The shape of Training data: ((3847, 64, 64, 3), (3847, 1)) The shape of Validation data: ((428, 64, 64, 3), (428, 1)) The shape of Testing data: ((475, 64, 64, 3), (475, 1))
Encode the target labels¶
Here, I am using LabelBinarizer,is another technique used to encode the target variables which reduces the sparsity as compared to one hot encoder.
#Using LabelBinarizer() to encode the target labels
# Storing the LabelBinarizer function in lb variable
lb = LabelBinarizer()
# Applying fit_transform on train target variable
y_train_e = lb.fit_transform(y_train)
# Applying only transform on test target variable
y_test_e = lb.transform(y_test)
y_val_e = lb.transform(y_val)
#checking the shape of encoded labels
y_train_e.shape,y_test_e.shape,y_val_e.shape
print("The shape of encoded Training data:",(y_train_e.shape))
print("The shape of encoded Validation data:", (y_val_e.shape))
print("The shape of encoded Testing data:", (y_test_e.shape))
The shape of encoded Training data: (3847, 12) The shape of encoded Validation data: (428, 12) The shape of encoded Testing data: (475, 12)
Data Normalization¶
Since the image pixel values range from 0-255, our method of normalization here will be scaling - we shall divide all the pixel values by 255 to standardize the images to have values between 0-1.
#Normalizing the data to a range of 0-1
X_train_normalized = X_train.astype('float32') / 255.0
X_val_normalized = X_val.astype('float32') / 255.0
X_test_normalized = X_test.astype('float32') / 255.0
Model Building¶
CNN (Convolutional Neural Network)With SGD Optimizer
#Clear the keras session
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(42)
import random
random.seed(42)
tf.random.set_seed(42)
Initialize model
- Filters: 256- Number of filters in the first hidden layer.This is also called as Kernel
- Kernel_Size: The kernel size here refers to the widthxheight of the filter mask. The kernel_size must be an odd integer as well. Typical values for kernel_size include: (1, 1) , (3, 3) , (5, 5) , (7, 7)
- Padding: The padding type is called SAME because the output size is the same as the input size(when stride=1). Using 'SAME' ensures that the filter is applied to all the elements of the input. Normally, padding is set to "SAME" while training the model. Output size is mathematically convenient for further computation.
- MaxPool2D: Max Pooling is a pooling operation that calculates the maximum value for patches of a feature map, and uses it to create a downsampled (pooled) feature map. It is usually used after a convolutional layer.
- Flatten: Flattening is converting the data into a 1-dimensional array for inputting it to the next layer. We flatten the output of the convolutional layers to create a single long feature vector. And it is connected to the final classification model, which is called a fully-connected layer.
#Intializing a sequential model with SGD optimizer
model_1= Sequential()
# Adding first conv layer with 128 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension 64x64x3
model_1.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(64, 64,3)))
# Adding max pooling to reduce the size of output of first conv layer
model_1.add(MaxPooling2D((2, 2), padding = 'same'))
model_1.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model_1.add(MaxPooling2D((2, 2), padding = 'same'))
model_1.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model_1.add(MaxPooling2D((2, 2), padding = 'same'))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model_1.add(Flatten())
# Adding a fully connected dense layer with 100 neurons
model_1.add(Dense(100, activation='relu'))
# Adding the output layer with 10 neurons and activation functions as softmax since this is a multi-class classification problem
model_1.add(Dense(12, activation='softmax'))
# Using SGD Optimizer
opt = SGD(learning_rate=0.01, momentum=0.9)
# Compile model
model_1.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model_1.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 128) │ 3,584 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 32, 32, 64) │ 73,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 16, 16, 32) │ 18,464 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 100) │ 204,900 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 12) │ 1,212 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 301,952 (1.15 MB)
Trainable params: 301,952 (1.15 MB)
Non-trainable params: 0 (0.00 B)
# Complete the code to fit the model on train and also using the validation data for validation
history_1 = model_1.fit(
X_train_normalized,
y_train_e,
epochs=30,
validation_data=(X_val_normalized,y_val_e),
batch_size=32,
verbose=2
)
Epoch 1/30 121/121 - 10s - 83ms/step - accuracy: 0.1258 - loss: 2.4343 - val_accuracy: 0.1379 - val_loss: 2.4177 Epoch 2/30 121/121 - 2s - 20ms/step - accuracy: 0.1562 - loss: 2.3993 - val_accuracy: 0.1799 - val_loss: 2.3615 Epoch 3/30 121/121 - 1s - 10ms/step - accuracy: 0.2417 - loss: 2.2017 - val_accuracy: 0.3411 - val_loss: 1.8018 Epoch 4/30 121/121 - 1s - 10ms/step - accuracy: 0.3858 - loss: 1.7294 - val_accuracy: 0.3879 - val_loss: 1.7243 Epoch 5/30 121/121 - 1s - 10ms/step - accuracy: 0.4793 - loss: 1.5108 - val_accuracy: 0.5304 - val_loss: 1.3907 Epoch 6/30 121/121 - 1s - 11ms/step - accuracy: 0.5760 - loss: 1.2294 - val_accuracy: 0.6051 - val_loss: 1.1603 Epoch 7/30 121/121 - 2s - 20ms/step - accuracy: 0.6480 - loss: 1.0065 - val_accuracy: 0.6589 - val_loss: 1.0269 Epoch 8/30 121/121 - 1s - 10ms/step - accuracy: 0.7044 - loss: 0.8462 - val_accuracy: 0.6379 - val_loss: 1.1014 Epoch 9/30 121/121 - 1s - 10ms/step - accuracy: 0.7341 - loss: 0.7400 - val_accuracy: 0.6822 - val_loss: 1.0220 Epoch 10/30 121/121 - 1s - 10ms/step - accuracy: 0.7583 - loss: 0.6773 - val_accuracy: 0.6963 - val_loss: 0.9496 Epoch 11/30 121/121 - 1s - 10ms/step - accuracy: 0.7798 - loss: 0.6029 - val_accuracy: 0.7033 - val_loss: 0.9883 Epoch 12/30 121/121 - 1s - 10ms/step - accuracy: 0.7845 - loss: 0.5970 - val_accuracy: 0.6846 - val_loss: 0.9320 Epoch 13/30 121/121 - 1s - 10ms/step - accuracy: 0.8230 - loss: 0.4880 - val_accuracy: 0.7407 - val_loss: 0.9139 Epoch 14/30 121/121 - 1s - 11ms/step - accuracy: 0.8513 - loss: 0.4118 - val_accuracy: 0.6729 - val_loss: 0.9932 Epoch 15/30 121/121 - 1s - 11ms/step - accuracy: 0.8487 - loss: 0.4025 - val_accuracy: 0.6028 - val_loss: 1.3877 Epoch 16/30 121/121 - 1s - 12ms/step - accuracy: 0.8737 - loss: 0.3489 - val_accuracy: 0.7033 - val_loss: 1.3718 Epoch 17/30 121/121 - 1s - 11ms/step - accuracy: 0.8903 - loss: 0.2951 - val_accuracy: 0.7734 - val_loss: 0.9690 Epoch 18/30 121/121 - 1s - 10ms/step - accuracy: 0.8947 - loss: 0.2873 - val_accuracy: 0.7243 - val_loss: 1.1300 Epoch 19/30 121/121 - 1s - 10ms/step - accuracy: 0.9186 - loss: 0.2219 - val_accuracy: 0.7079 - val_loss: 1.3798 Epoch 20/30 121/121 - 1s - 10ms/step - accuracy: 0.9111 - loss: 0.2482 - val_accuracy: 0.7126 - val_loss: 1.4509 Epoch 21/30 121/121 - 1s - 10ms/step - accuracy: 0.9262 - loss: 0.2125 - val_accuracy: 0.6869 - val_loss: 1.4123 Epoch 22/30 121/121 - 1s - 10ms/step - accuracy: 0.9381 - loss: 0.1675 - val_accuracy: 0.7383 - val_loss: 1.1488 Epoch 23/30 121/121 - 1s - 11ms/step - accuracy: 0.9353 - loss: 0.1801 - val_accuracy: 0.7243 - val_loss: 1.2429 Epoch 24/30 121/121 - 1s - 10ms/step - accuracy: 0.9384 - loss: 0.1672 - val_accuracy: 0.7593 - val_loss: 1.1076 Epoch 25/30 121/121 - 1s - 11ms/step - accuracy: 0.9594 - loss: 0.1185 - val_accuracy: 0.7220 - val_loss: 1.3750 Epoch 26/30 121/121 - 3s - 21ms/step - accuracy: 0.9597 - loss: 0.1169 - val_accuracy: 0.7009 - val_loss: 1.4292 Epoch 27/30 121/121 - 1s - 10ms/step - accuracy: 0.9579 - loss: 0.1254 - val_accuracy: 0.6051 - val_loss: 2.0654 Epoch 28/30 121/121 - 1s - 10ms/step - accuracy: 0.9579 - loss: 0.1251 - val_accuracy: 0.7664 - val_loss: 1.3702 Epoch 29/30 121/121 - 1s - 10ms/step - accuracy: 0.9514 - loss: 0.1397 - val_accuracy: 0.7827 - val_loss: 1.3468 Epoch 30/30 121/121 - 1s - 11ms/step - accuracy: 0.9493 - loss: 0.1381 - val_accuracy: 0.7664 - val_loss: 1.3067
#Observing Accuracy and Loss response for both Training and validation data
acc = history_1.history['accuracy']
val_acc = history_1.history['val_accuracy']
loss = history_1.history['loss']
val_loss = history_1.history['val_loss']
epochs=30
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Observations:
- From the above responses,it shows clearly that model is overfitting.
- It's training accuracy(97%) is more more than validation accuracy which is 77%.
- There is vast difference between training loss and validation loss.
Evaluating the model on test data
accuracy = model_1.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 1s - 66ms/step - accuracy: 0.7579 - loss: 1.4049
Generating the predictions using test data
# Here we would get the output as probablities for each category
y_pred=model_1.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 1s 27ms/step
y_pred
array([[3.01892639e-14, 2.39907379e-18, 5.96572011e-12, ...,
4.20202415e-15, 2.58122483e-14, 7.74058823e-13],
[2.59416522e-21, 6.68598805e-05, 5.70869574e-08, ...,
5.98243375e-08, 9.99933004e-01, 1.78954914e-16],
[4.39574217e-23, 6.35973774e-11, 1.43191847e-10, ...,
4.84382201e-10, 1.00000000e+00, 4.76497599e-19],
...,
[9.43332240e-02, 5.49181456e-09, 3.65056714e-08, ...,
8.84941120e-10, 1.13484075e-08, 6.75703227e-09],
[3.22282544e-12, 3.38592116e-07, 3.89111447e-05, ...,
2.81373356e-02, 7.50897747e-11, 1.85555916e-06],
[1.05874837e-16, 3.66885047e-07, 1.05787969e-10, ...,
9.96402621e-01, 6.04178922e-06, 1.31685267e-08]], dtype=float32)
y_test_pred_classes_ln = np.argmax(y_pred, axis=1)
normal_y_test = np.argmax(y_test_e, axis=1)
accuracy_score(normal_y_test,y_test_pred_classes_ln)
0.7578947368421053
Plotting Confusion Matrix
- The Confusion matrix is also defined as an inbuilt function in the TensorFlow module, so we can use that for evaluating the classification model.
- The Confusion matrix expects categorical data as input. However, y_test_encoded is an encoded value, whereas y_pred has probabilities. So,we must retrieve the categorical values from the encoded values.
- We will use the argmax() function to obtain the maximum value over each category on both y_test_encoded and y_pred and obtain their respective classes.
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
from sklearn import metrics
#Creating Classification report
cr1=metrics.classification_report(y_test_arg, y_pred_arg)
print(cr1)
precision recall f1-score support
0 0.47 0.27 0.34 26
1 0.73 0.95 0.82 39
2 0.88 0.72 0.79 29
3 0.88 0.82 0.85 61
4 0.77 0.45 0.57 22
5 0.84 0.79 0.82 48
6 0.67 0.89 0.76 65
7 0.55 0.55 0.55 22
8 0.67 0.77 0.71 52
9 0.83 0.65 0.73 23
10 0.92 0.88 0.90 50
11 0.80 0.74 0.77 38
accuracy 0.76 475
macro avg 0.75 0.71 0.72 475
weighted avg 0.76 0.76 0.75 475
Observations:
- Here,I will get 75% accuracy score for test data.
- From the confusion matrix,we can say that common chickweek,loose silky-bent,small-flowered cranesbill predicting pretty well but still loose silk-bent gives false alarm for black-grass,common chickweed,,common wheat,fat-hen,maizeshepered purse and sugar beet.
- By examining the matrix, you can see which plant species are being confused with each other and get a sense of the overall accuracy of the model.
- You can see that the model is better at identifying some species, like Loose Silky-bent or Scentless Mayweed, and struggles with other species, like Common Chickweed, where a significant number were misclassified as Common Wheat or Small-flowered Cranesbill.
Model 2 with Adam optimizer
#Clear keras
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(42)
import random
random.seed(42)
tf.random.set_seed(42)
#Intializing a sequential model
model_2= Sequential()
# Adding first conv layer with 128 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension 64x64x3
model_2.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(64, 64,3)))
# Adding max pooling to reduce the size of output of first conv layer
model_2.add(MaxPooling2D((2, 2), padding = 'same'))
model_2.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model_2.add(MaxPooling2D((2, 2), padding = 'same'))
model_2.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model_2.add(MaxPooling2D((2, 2), padding = 'same'))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model_2.add(Flatten())
# Adding a fully connected dense layer with 100 neurons
model_2.add(Dense(100, activation='relu'))
# Adding the output layer with 10 neurons and activation functions as softmax since this is a multi-class classification problem
model_2.add(Dense(12, activation='softmax'))
# Using adam optimizer
opt = Adam()
# Compile model
model_2.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model_2.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 128) │ 3,584 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 32, 32, 64) │ 73,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 16, 16, 32) │ 18,464 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 100) │ 204,900 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 12) │ 1,212 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 301,952 (1.15 MB)
Trainable params: 301,952 (1.15 MB)
Non-trainable params: 0 (0.00 B)
# Complete the code to fit the model on train and also using the validation data for validation
history_2 = model_2.fit(
X_train_normalized,
y_train_e,
epochs=30,
validation_data=(X_val_normalized,y_val_e),
batch_size=32,
verbose=2
)
Epoch 1/30 121/121 - 7s - 59ms/step - accuracy: 0.1479 - loss: 2.4168 - val_accuracy: 0.2593 - val_loss: 2.2400 Epoch 2/30 121/121 - 6s - 53ms/step - accuracy: 0.3800 - loss: 1.8161 - val_accuracy: 0.4509 - val_loss: 1.5465 Epoch 3/30 121/121 - 2s - 21ms/step - accuracy: 0.5571 - loss: 1.2864 - val_accuracy: 0.5584 - val_loss: 1.2817 Epoch 4/30 121/121 - 1s - 10ms/step - accuracy: 0.6538 - loss: 1.0048 - val_accuracy: 0.5888 - val_loss: 1.1644 Epoch 5/30 121/121 - 1s - 10ms/step - accuracy: 0.7122 - loss: 0.8326 - val_accuracy: 0.6355 - val_loss: 1.0442 Epoch 6/30 121/121 - 1s - 12ms/step - accuracy: 0.7434 - loss: 0.7404 - val_accuracy: 0.6355 - val_loss: 1.1313 Epoch 7/30 121/121 - 1s - 11ms/step - accuracy: 0.7650 - loss: 0.6663 - val_accuracy: 0.6682 - val_loss: 1.0809 Epoch 8/30 121/121 - 2s - 19ms/step - accuracy: 0.7931 - loss: 0.5782 - val_accuracy: 0.6963 - val_loss: 0.9101 Epoch 9/30 121/121 - 1s - 10ms/step - accuracy: 0.8071 - loss: 0.5525 - val_accuracy: 0.7220 - val_loss: 0.9220 Epoch 10/30 121/121 - 1s - 11ms/step - accuracy: 0.8240 - loss: 0.5029 - val_accuracy: 0.7360 - val_loss: 0.9403 Epoch 11/30 121/121 - 1s - 10ms/step - accuracy: 0.8295 - loss: 0.4607 - val_accuracy: 0.7220 - val_loss: 0.9536 Epoch 12/30 121/121 - 1s - 11ms/step - accuracy: 0.8563 - loss: 0.4140 - val_accuracy: 0.6986 - val_loss: 1.0445 Epoch 13/30 121/121 - 2s - 20ms/step - accuracy: 0.8744 - loss: 0.3673 - val_accuracy: 0.7453 - val_loss: 0.8673 Epoch 14/30 121/121 - 1s - 11ms/step - accuracy: 0.8848 - loss: 0.3117 - val_accuracy: 0.7196 - val_loss: 0.9826 Epoch 15/30 121/121 - 1s - 11ms/step - accuracy: 0.8825 - loss: 0.3161 - val_accuracy: 0.7430 - val_loss: 1.0250 Epoch 16/30 121/121 - 2s - 19ms/step - accuracy: 0.8942 - loss: 0.2870 - val_accuracy: 0.7383 - val_loss: 1.0495 Epoch 17/30 121/121 - 1s - 11ms/step - accuracy: 0.8939 - loss: 0.2825 - val_accuracy: 0.7290 - val_loss: 1.1535 Epoch 18/30 121/121 - 1s - 11ms/step - accuracy: 0.8926 - loss: 0.2931 - val_accuracy: 0.7453 - val_loss: 0.9573 Epoch 19/30 121/121 - 3s - 21ms/step - accuracy: 0.9168 - loss: 0.2262 - val_accuracy: 0.6986 - val_loss: 1.3098 Epoch 20/30 121/121 - 1s - 10ms/step - accuracy: 0.9306 - loss: 0.1893 - val_accuracy: 0.7033 - val_loss: 1.1435 Epoch 21/30 121/121 - 1s - 10ms/step - accuracy: 0.9470 - loss: 0.1547 - val_accuracy: 0.7150 - val_loss: 1.4555 Epoch 22/30 121/121 - 1s - 10ms/step - accuracy: 0.9589 - loss: 0.1243 - val_accuracy: 0.7383 - val_loss: 1.3425 Epoch 23/30 121/121 - 1s - 11ms/step - accuracy: 0.9571 - loss: 0.1076 - val_accuracy: 0.7477 - val_loss: 1.3380 Epoch 24/30 121/121 - 2s - 20ms/step - accuracy: 0.9693 - loss: 0.0898 - val_accuracy: 0.7757 - val_loss: 1.3024 Epoch 25/30 121/121 - 1s - 10ms/step - accuracy: 0.9670 - loss: 0.0989 - val_accuracy: 0.7780 - val_loss: 1.4491 Epoch 26/30 121/121 - 1s - 10ms/step - accuracy: 0.9667 - loss: 0.0959 - val_accuracy: 0.7734 - val_loss: 1.3892 Epoch 27/30 121/121 - 1s - 11ms/step - accuracy: 0.9623 - loss: 0.1073 - val_accuracy: 0.7593 - val_loss: 1.3325 Epoch 28/30 121/121 - 1s - 11ms/step - accuracy: 0.9701 - loss: 0.0901 - val_accuracy: 0.7664 - val_loss: 1.4338 Epoch 29/30 121/121 - 1s - 10ms/step - accuracy: 0.9670 - loss: 0.0984 - val_accuracy: 0.7827 - val_loss: 1.5190 Epoch 30/30 121/121 - 1s - 10ms/step - accuracy: 0.9628 - loss: 0.1090 - val_accuracy: 0.7360 - val_loss: 1.6908
#Observing Accuracy and Loss response for both Training and validation data
acc = history_2.history['accuracy']
val_acc = history_2.history['val_accuracy']
loss = history_2.history['loss']
val_loss = history_2.history['val_loss']
epochs=30
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Observations
- This model shows overfitting,too.
- Training accuracy is littlebit improved from 97% to 98% but validation accuracy reduced from 76.6% to 73%.
Evaluating model with Test data
#Calculating accuracy for test data
accuracy = model_2.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 0s - 26ms/step - accuracy: 0.7179 - loss: 1.7077
# Here we would get the output as probablities for each category
y_pred=model_2.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 1s 24ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
#creating classification report
cr2=metrics.classification_report(y_test_arg, y_pred_arg)
print(cr2)
precision recall f1-score support
0 0.30 0.62 0.40 26
1 0.90 0.92 0.91 39
2 0.68 0.86 0.76 29
3 0.91 0.82 0.86 61
4 0.64 0.41 0.50 22
5 0.83 0.60 0.70 48
6 0.63 0.55 0.59 65
7 0.60 0.82 0.69 22
8 0.74 0.75 0.74 52
9 0.76 0.57 0.65 23
10 0.84 0.84 0.84 50
11 0.85 0.74 0.79 38
accuracy 0.72 475
macro avg 0.72 0.71 0.70 475
weighted avg 0.75 0.72 0.72 475
Observations
- For this model,accuracy got reduced compared to previous model (75% to 72%).
- This model shows poor predictions than first model.
Model 3 with Batch Normalization and Dropout Layer
# Clear the keras kernel
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(42)
import random
random.seed(42)
tf.random.set_seed(42)
# Intializing a sequential model
model_3 = Sequential()
# Complete the code to add the first conv layer with 64 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension images
model_3.add(Conv2D(64, (3,3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Complete the code to add max pooling to reduce the size of output of first conv layer
model_3.add(MaxPooling2D((2, 2), padding = 'same'))
model_3.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model_3.add(MaxPooling2D((2, 2), padding = 'same'))
model_3.add(BatchNormalization())
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model_3.add(Flatten())
# Adding a fully connected dense layer with 16 neurons
model_3.add(Dense(16, activation='relu'))
# Complete the code to add dropout with dropout_rate=0.3
model_3.add(Dropout(0.3))
# Complete the code to add the output layer with 12 neurons and activation functions as softmax since this is a multi-class classification problem
model_3.add(Dense(12, activation='softmax'))
# Using adam optimizer
opt = Adam()
# Complete the code to Compile model
model_3.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model_3.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 64) │ 1,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 32, 32, 32) │ 18,464 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization │ (None, 16, 16, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 8192) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 16) │ 131,088 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 16) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 12) │ 204 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 151,676 (592.48 KB)
Trainable params: 151,612 (592.23 KB)
Non-trainable params: 64 (256.00 B)
history_3= model_3.fit(
X_train_normalized,
y_train_e,
epochs=30,
validation_data=(X_val_normalized,y_val_e),
batch_size=32,
verbose=2
)
Epoch 1/30 121/121 - 8s - 70ms/step - accuracy: 0.2568 - loss: 2.2080 - val_accuracy: 0.2547 - val_loss: 2.3815 Epoch 2/30 121/121 - 1s - 7ms/step - accuracy: 0.4364 - loss: 1.7018 - val_accuracy: 0.2967 - val_loss: 2.2839 Epoch 3/30 121/121 - 1s - 7ms/step - accuracy: 0.4908 - loss: 1.5130 - val_accuracy: 0.5350 - val_loss: 1.8559 Epoch 4/30 121/121 - 1s - 10ms/step - accuracy: 0.5407 - loss: 1.3599 - val_accuracy: 0.4836 - val_loss: 1.5388 Epoch 5/30 121/121 - 1s - 6ms/step - accuracy: 0.5716 - loss: 1.2250 - val_accuracy: 0.5724 - val_loss: 1.3300 Epoch 6/30 121/121 - 1s - 10ms/step - accuracy: 0.6119 - loss: 1.1170 - val_accuracy: 0.5631 - val_loss: 1.3338 Epoch 7/30 121/121 - 1s - 7ms/step - accuracy: 0.6382 - loss: 1.0235 - val_accuracy: 0.5724 - val_loss: 1.2981 Epoch 8/30 121/121 - 1s - 10ms/step - accuracy: 0.6597 - loss: 0.9441 - val_accuracy: 0.6355 - val_loss: 1.1752 Epoch 9/30 121/121 - 1s - 7ms/step - accuracy: 0.6860 - loss: 0.8753 - val_accuracy: 0.6308 - val_loss: 1.1458 Epoch 10/30 121/121 - 1s - 7ms/step - accuracy: 0.7078 - loss: 0.8025 - val_accuracy: 0.4603 - val_loss: 2.0573 Epoch 11/30 121/121 - 2s - 13ms/step - accuracy: 0.7213 - loss: 0.7810 - val_accuracy: 0.2664 - val_loss: 3.4514 Epoch 12/30 121/121 - 1s - 8ms/step - accuracy: 0.7260 - loss: 0.7477 - val_accuracy: 0.5818 - val_loss: 1.2458 Epoch 13/30 121/121 - 1s - 10ms/step - accuracy: 0.7489 - loss: 0.6945 - val_accuracy: 0.5935 - val_loss: 1.3133 Epoch 14/30 121/121 - 1s - 9ms/step - accuracy: 0.7481 - loss: 0.6824 - val_accuracy: 0.6028 - val_loss: 1.2568 Epoch 15/30 121/121 - 1s - 8ms/step - accuracy: 0.7596 - loss: 0.6504 - val_accuracy: 0.6682 - val_loss: 1.1224 Epoch 16/30 121/121 - 1s - 8ms/step - accuracy: 0.7603 - loss: 0.6409 - val_accuracy: 0.6238 - val_loss: 1.2345 Epoch 17/30 121/121 - 1s - 9ms/step - accuracy: 0.7801 - loss: 0.5962 - val_accuracy: 0.6682 - val_loss: 1.1338 Epoch 18/30 121/121 - 1s - 10ms/step - accuracy: 0.7918 - loss: 0.5579 - val_accuracy: 0.6168 - val_loss: 1.3030 Epoch 19/30 121/121 - 1s - 10ms/step - accuracy: 0.7855 - loss: 0.5697 - val_accuracy: 0.5748 - val_loss: 1.4000 Epoch 20/30 121/121 - 1s - 7ms/step - accuracy: 0.7923 - loss: 0.5675 - val_accuracy: 0.6612 - val_loss: 1.1863 Epoch 21/30 121/121 - 1s - 7ms/step - accuracy: 0.8056 - loss: 0.5168 - val_accuracy: 0.6425 - val_loss: 1.2811 Epoch 22/30 121/121 - 1s - 11ms/step - accuracy: 0.8084 - loss: 0.5028 - val_accuracy: 0.5397 - val_loss: 1.8287 Epoch 23/30 121/121 - 1s - 12ms/step - accuracy: 0.8004 - loss: 0.5284 - val_accuracy: 0.6565 - val_loss: 1.5725 Epoch 24/30 121/121 - 1s - 10ms/step - accuracy: 0.8191 - loss: 0.4987 - val_accuracy: 0.5561 - val_loss: 2.1645 Epoch 25/30 121/121 - 1s - 7ms/step - accuracy: 0.8193 - loss: 0.4841 - val_accuracy: 0.5210 - val_loss: 1.8085 Epoch 26/30 121/121 - 1s - 11ms/step - accuracy: 0.8141 - loss: 0.4903 - val_accuracy: 0.6776 - val_loss: 1.2680 Epoch 27/30 121/121 - 1s - 10ms/step - accuracy: 0.8305 - loss: 0.4426 - val_accuracy: 0.6192 - val_loss: 1.5599 Epoch 28/30 121/121 - 1s - 10ms/step - accuracy: 0.8362 - loss: 0.4282 - val_accuracy: 0.6308 - val_loss: 2.1470 Epoch 29/30 121/121 - 1s - 10ms/step - accuracy: 0.8349 - loss: 0.4327 - val_accuracy: 0.3785 - val_loss: 3.4958 Epoch 30/30 121/121 - 1s - 7ms/step - accuracy: 0.8417 - loss: 0.4029 - val_accuracy: 0.4556 - val_loss: 2.5347
#Observing Accuracy and Loss response for both Training and validation data
acc = history_3.history['accuracy']
val_acc = history_3.history['val_accuracy']
loss = history_3.history['loss']
val_loss = history_3.history['val_loss']
epochs=30
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Observations:
- This model also shows presence of overfitting.
- Accuracy and loss response for training as well as validation data has degraded.
Evaluating Test Data
#Calculating accuracy for test data
accuracy = model_3.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 0s - 24ms/step - accuracy: 0.4653 - loss: 2.5514
# Here we would get the output as probablities for each category
y_pred=model_3.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
# Plotting the classification report
from sklearn import metrics
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.27 0.12 0.16 26
1 1.00 0.31 0.47 39
2 1.00 0.03 0.07 29
3 0.41 0.98 0.58 61
4 1.00 0.05 0.09 22
5 0.78 0.29 0.42 48
6 0.45 0.88 0.60 65
7 0.67 0.36 0.47 22
8 0.66 0.40 0.50 52
9 0.19 0.17 0.18 23
10 0.64 0.78 0.70 50
11 0.58 0.50 0.54 38
accuracy 0.50 475
macro avg 0.64 0.41 0.40 475
weighted avg 0.62 0.50 0.46 475
Observations:
- Accuracy and loss for test data has degraded.
- Performance is not improved.
Model Performance Improvement¶
Reducing the Learning Rate:
Hint: Use ReduceLRonPlateau() function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
# Defining learning rate reduction to monitor val_accuracy
learning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy',
patience=3,
verbose=1,
factor=0.5,
min_lr=0.00001)
Data Augmentation¶
In most of the real-world case studies, it is challenging to acquire a large number of images and then train CNNs. To overcome this problem, one approach we might consider is Data Augmentation. CNNs have the property of translational invariance, which means they can recognise an object even if its appearance shifts translationally in some way. Taking this attribute into account, we can augment the images using the techniques listed below -
- Horizontal Flip (should be set to True/False)
- Vertical Flip (should be set to True/False)
- Height Shift (should be between 0 and 1)
- Width Shift (should be between 0 and 1)
- Rotation (should be between 0 and 180)
- Shear (should be between 0 and 1)
- Zoom (should be between 0 and 1) etc.
Remember, data augmentation should not be used in the validation/test data set.
# Clearing backend
from tensorflow.keras import backend
backend.clear_session()
# Fixing the seed for random number generators
import random
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
#Defining data generator
train_datagen = ImageDataGenerator(
rotation_range=45,
shear_range=0.15,
zoom_range=0.15,
width_shift_range=0.15,
height_shift_range=0.15,
vertical_flip=True,
horizontal_flip=True,
fill_mode='nearest'
)
# Intializing a sequential model
model_4 = Sequential()
# Complete the code to add the first conv layer with 64 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension images
model_4.add(Conv2D(64, (3,3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Complete the code to add max pooling to reduce the size of output of first conv layer
model_4.add(MaxPooling2D((2, 2)))
model_4.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model_4.add(MaxPooling2D((2, 2), padding = 'same'))
model_4.add(BatchNormalization())
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model_4.add(Flatten())
# Adding a fully connected dense layer with 16 neurons
model_4.add(Dense(16, activation='relu'))
# Complete the code to add dropout with dropout_rate=0.3
model_4.add(Dropout(0.3))
# Complete the code to add the output layer with 12 neurons and activation functions as softmax since this is a multi-class classification problem
model_4.add(Dense(12, activation='softmax'))
# Complete the code to initialize Adam Optimimzer
opt=Adam()
# Complete the code to Compile model
model_4.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model_4.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 64) │ 1,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 32, 32, 32) │ 18,464 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization │ (None, 16, 16, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 8192) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 16) │ 131,088 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 16) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 12) │ 204 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 151,676 (592.48 KB)
Trainable params: 151,612 (592.23 KB)
Non-trainable params: 64 (256.00 B)
# Epochs
epochs = 30
# Batch size
batch_size = 64
history_4= model_4.fit(train_datagen.flow(X_train_normalized,y_train_e,
batch_size=batch_size,
seed=42,
shuffle=False),
epochs=epochs,
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
validation_data=(X_val_normalized,y_val_e),
verbose=1)
Epoch 1/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 13s 136ms/step - accuracy: 0.1490 - loss: 2.4192 - val_accuracy: 0.3201 - val_loss: 2.4179 Epoch 2/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.3594 - loss: 1.9990 - val_accuracy: 0.2196 - val_loss: 2.4226 Epoch 3/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 17s 114ms/step - accuracy: 0.3318 - loss: 2.0164 - val_accuracy: 0.3621 - val_loss: 2.3142 Epoch 4/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.3594 - loss: 2.1557 - val_accuracy: 0.3645 - val_loss: 2.3198 Epoch 5/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 5s 86ms/step - accuracy: 0.3719 - loss: 1.8596 - val_accuracy: 0.4299 - val_loss: 2.2566 Epoch 6/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.3750 - loss: 1.8815 - val_accuracy: 0.4322 - val_loss: 2.2716 Epoch 7/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 113ms/step - accuracy: 0.4083 - loss: 1.7504 - val_accuracy: 0.1963 - val_loss: 2.2835 Epoch 8/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.3281 - loss: 1.7983 - val_accuracy: 0.1939 - val_loss: 2.2894 Epoch 9/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 5s 88ms/step - accuracy: 0.4299 - loss: 1.6481 - val_accuracy: 0.3692 - val_loss: 2.0432 Epoch 10/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.3906 - loss: 1.6878 - val_accuracy: 0.3505 - val_loss: 2.0544 Epoch 11/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 98ms/step - accuracy: 0.4658 - loss: 1.5642 - val_accuracy: 0.4112 - val_loss: 1.8191 Epoch 12/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.5312 - loss: 1.4085 - val_accuracy: 0.4136 - val_loss: 1.8075 Epoch 13/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 103ms/step - accuracy: 0.4770 - loss: 1.5281 - val_accuracy: 0.5911 - val_loss: 1.5865 Epoch 14/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.4219 - loss: 1.6598 - val_accuracy: 0.5748 - val_loss: 1.5991 Epoch 15/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 5s 87ms/step - accuracy: 0.4700 - loss: 1.4971 - val_accuracy: 0.4463 - val_loss: 1.5538 Epoch 16/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.6250 - loss: 1.2695 - val_accuracy: 0.4579 - val_loss: 1.5498 Epoch 17/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 87ms/step - accuracy: 0.4910 - loss: 1.4515 - val_accuracy: 0.4883 - val_loss: 1.4290 Epoch 18/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.4844 - loss: 1.5012 - val_accuracy: 0.4533 - val_loss: 1.5121 Epoch 19/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 88ms/step - accuracy: 0.5193 - loss: 1.4318 - val_accuracy: 0.5631 - val_loss: 1.2917 Epoch 20/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.5312 - loss: 1.3824 - val_accuracy: 0.5257 - val_loss: 1.3703 Epoch 21/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 104ms/step - accuracy: 0.5359 - loss: 1.3387 - val_accuracy: 0.5257 - val_loss: 1.4483 Epoch 22/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.4844 - loss: 1.5457 - val_accuracy: 0.5421 - val_loss: 1.3908 Epoch 23/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 115ms/step - accuracy: 0.5232 - loss: 1.3762 - val_accuracy: 0.5140 - val_loss: 1.3808 Epoch 24/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.5938 - loss: 1.3516 - val_accuracy: 0.4556 - val_loss: 1.5926 Epoch 25/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 5s 87ms/step - accuracy: 0.5469 - loss: 1.3339 - val_accuracy: 0.6215 - val_loss: 1.1498 Epoch 26/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.5938 - loss: 1.3153 - val_accuracy: 0.5911 - val_loss: 1.2445 Epoch 27/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 102ms/step - accuracy: 0.5552 - loss: 1.2758 - val_accuracy: 0.4206 - val_loss: 2.2375 Epoch 28/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.5938 - loss: 1.1607 - val_accuracy: 0.4346 - val_loss: 2.0631 Epoch 29/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 115ms/step - accuracy: 0.5406 - loss: 1.2975 - val_accuracy: 0.5935 - val_loss: 1.1834 Epoch 30/30 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.6250 - loss: 1.0961 - val_accuracy: 0.5818 - val_loss: 1.2555
#Observing Accuracy and Loss response for both Training and validation data
acc = history_4.history['accuracy']
val_acc = history_4.history['val_accuracy']
loss = history_4.history['loss']
val_loss = history_4.history['val_loss']
epochs=30
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Observations:
- Probelm of overfitting has improved with Data augmentation.
- Validation accuracy is improved over training data.Also,validation loss is improved over training.
- But model is not performing well as we accepted.Training accuracy and validation accuracy is 54% and 59.6%.
Evaluating Test Data
#Calculating accuracy for test data
accuracy = model_4.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 0s - 24ms/step - accuracy: 0.5789 - loss: 1.2028
# Here we would get the output as probablities for each category
y_pred=model_4.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 1s 33ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
# Plotting the classification report
from sklearn import metrics
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.31 0.42 0.35 26
1 0.73 0.56 0.64 39
2 0.54 0.90 0.68 29
3 0.78 0.84 0.81 61
4 0.70 0.32 0.44 22
5 0.41 0.42 0.41 48
6 0.57 0.71 0.63 65
7 1.00 0.77 0.87 22
8 0.86 0.12 0.20 52
9 1.00 0.04 0.08 23
10 0.49 0.98 0.65 50
11 0.61 0.50 0.55 38
accuracy 0.58 475
macro avg 0.67 0.55 0.53 475
weighted avg 0.65 0.58 0.54 475
Observations:
- It gives false alarm for black Grass,sugar beet,scented mayweed,sheperd purse,loose,silkbent,commonweed,etc,which are more than 60% plant categories which is not good.
Model_5 with Data Augmentation
#Clear keras
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(42)
import random
random.seed(42)
tf.random.set_seed(42)
#Defining new model
model_5 = Sequential()
# Input_shape denotes input image dimension images
model_5.add(
Conv2D(64, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal', input_shape=(64, 64, 3))
)
model_5.add(BatchNormalization())
model_5.add(Conv2D(64, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model_5.add(BatchNormalization())
model_5.add(MaxPooling2D((2, 2), padding='same'))
model_5.add(Dropout(0.3))
model_5.add(Conv2D(256, (5, 5), activation='relu', padding="same", kernel_initializer='he_normal'))
model_5.add(BatchNormalization())
model_5.add(Conv2D(256, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model_5.add(BatchNormalization())
model_5.add(MaxPooling2D((2, 2), padding='same'))
model_5.add(Dropout(0.4))
model_5.add(Conv2D(32, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model_5.add(BatchNormalization())
model_5.add(MaxPooling2D((2, 2), padding='same'))
model_5.add(SpatialDropout2D(0.3))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model_5.add(Flatten())
model_5.add(Dense(128, activation='relu', kernel_regularizer='l2'))
model_5.add(Dropout(0.5))
model_5.add(Dense(32, activation='relu'))
model_5.add(Dense(12, activation='softmax'))
opt = Adam(learning_rate=0.001)
model_5.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model_5.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 64) │ 1,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization │ (None, 64, 64, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 64, 64, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_1 │ (None, 64, 64, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 32, 32, 256) │ 409,856 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_2 │ (None, 32, 32, 256) │ 1,024 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_3 (Conv2D) │ (None, 32, 32, 256) │ 590,080 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_3 │ (None, 32, 32, 256) │ 1,024 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 256) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_1 (Dropout) │ (None, 16, 16, 256) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_4 (Conv2D) │ (None, 16, 16, 32) │ 73,760 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_4 │ (None, 16, 16, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ spatial_dropout2d (SpatialDropout2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 128) │ 262,272 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_2 (Dropout) │ (None, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 32) │ 4,128 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_2 (Dense) │ (None, 12) │ 396 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 1,381,900 (5.27 MB)
Trainable params: 1,380,556 (5.27 MB)
Non-trainable params: 1,344 (5.25 KB)
epochs = 60
history= model_5.fit(train_datagen.flow(X_train_normalized,y_train_e,
batch_size=batch_size,
seed=42,
shuffle=False),
epochs=epochs,
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
validation_data=(X_val_normalized,y_val_e),
verbose=1)
Epoch 1/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 24s 240ms/step - accuracy: 0.1394 - loss: 5.2075 - val_accuracy: 0.1379 - val_loss: 5.0230 Epoch 2/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.1719 - loss: 4.1114 - val_accuracy: 0.1379 - val_loss: 5.0239 Epoch 3/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 126ms/step - accuracy: 0.2969 - loss: 3.7488 - val_accuracy: 0.1379 - val_loss: 6.1617 Epoch 4/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.2656 - loss: 3.2660 - val_accuracy: 0.1379 - val_loss: 6.1623 Epoch 5/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 109ms/step - accuracy: 0.3768 - loss: 3.0276 - val_accuracy: 0.1519 - val_loss: 5.4779 Epoch 6/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4375 - loss: 2.6179 - val_accuracy: 0.1519 - val_loss: 5.1828 Epoch 7/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 127ms/step - accuracy: 0.4127 - loss: 2.5841 - val_accuracy: 0.1659 - val_loss: 4.6261 Epoch 8/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.4219 - loss: 2.2991 - val_accuracy: 0.1706 - val_loss: 4.5607 Epoch 9/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 116ms/step - accuracy: 0.4780 - loss: 2.2174 - val_accuracy: 0.1682 - val_loss: 5.1267 Epoch 10/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.5625 - loss: 2.2592 - val_accuracy: 0.1846 - val_loss: 4.7413 Epoch 11/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 108ms/step - accuracy: 0.5196 - loss: 2.0006 - val_accuracy: 0.2523 - val_loss: 3.0364 Epoch 12/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.6562 - loss: 1.5372 - val_accuracy: 0.2897 - val_loss: 2.7943 Epoch 13/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 126ms/step - accuracy: 0.5460 - loss: 1.8313 - val_accuracy: 0.4720 - val_loss: 1.9465 Epoch 14/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.5625 - loss: 2.0422 - val_accuracy: 0.4766 - val_loss: 1.9264 Epoch 15/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 119ms/step - accuracy: 0.5820 - loss: 1.6943 - val_accuracy: 0.5724 - val_loss: 1.5735 Epoch 16/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.6719 - loss: 1.3517 - val_accuracy: 0.6005 - val_loss: 1.5307 Epoch 17/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 109ms/step - accuracy: 0.6341 - loss: 1.5112 - val_accuracy: 0.6799 - val_loss: 1.3798 Epoch 18/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.5781 - loss: 1.6002 - val_accuracy: 0.6916 - val_loss: 1.3863 Epoch 19/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 110ms/step - accuracy: 0.6434 - loss: 1.4477 - val_accuracy: 0.6519 - val_loss: 1.4339 Epoch 20/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.6562 - loss: 1.4044 - val_accuracy: 0.6565 - val_loss: 1.3970 Epoch 21/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 126ms/step - accuracy: 0.6748 - loss: 1.3568 - val_accuracy: 0.6379 - val_loss: 1.3881 Epoch 22/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.5625 - loss: 1.4951 - val_accuracy: 0.6636 - val_loss: 1.3319 Epoch 23/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 113ms/step - accuracy: 0.6620 - loss: 1.3602 - val_accuracy: 0.7313 - val_loss: 1.2435 Epoch 24/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7812 - loss: 1.0653 - val_accuracy: 0.7220 - val_loss: 1.2205 Epoch 25/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 106ms/step - accuracy: 0.6922 - loss: 1.2548 - val_accuracy: 0.7033 - val_loss: 1.2207 Epoch 26/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7188 - loss: 1.3020 - val_accuracy: 0.6939 - val_loss: 1.2541 Epoch 27/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 125ms/step - accuracy: 0.7080 - loss: 1.1829 - val_accuracy: 0.7523 - val_loss: 1.0168 Epoch 28/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.7031 - loss: 1.1100 - val_accuracy: 0.7360 - val_loss: 1.1061 Epoch 29/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 109ms/step - accuracy: 0.7192 - loss: 1.1232 - val_accuracy: 0.5818 - val_loss: 1.6302 Epoch 30/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.7031 - loss: 1.0542 - val_accuracy: 0.5818 - val_loss: 1.6712 Epoch 31/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 107ms/step - accuracy: 0.7467 - loss: 1.0420 - val_accuracy: 0.6542 - val_loss: 1.2517 Epoch 32/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.8281 - loss: 0.9057 - val_accuracy: 0.7173 - val_loss: 1.1597 Epoch 33/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 111ms/step - accuracy: 0.7180 - loss: 1.1159 - val_accuracy: 0.7570 - val_loss: 1.0526 Epoch 34/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8125 - loss: 0.9268 - val_accuracy: 0.7173 - val_loss: 1.1160 Epoch 35/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 124ms/step - accuracy: 0.7335 - loss: 1.1044 - val_accuracy: 0.7617 - val_loss: 1.1827 Epoch 36/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7188 - loss: 1.0026 - val_accuracy: 0.7547 - val_loss: 1.2367 Epoch 37/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 119ms/step - accuracy: 0.7544 - loss: 1.0169 - val_accuracy: 0.7103 - val_loss: 1.2661 Epoch 38/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7500 - loss: 1.0177 - val_accuracy: 0.7243 - val_loss: 1.1289 Epoch 39/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 107ms/step - accuracy: 0.7649 - loss: 0.9586 - val_accuracy: 0.7523 - val_loss: 1.1387 Epoch 40/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.7656 - loss: 0.9978 - val_accuracy: 0.7570 - val_loss: 1.1712 Epoch 41/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 125ms/step - accuracy: 0.7715 - loss: 0.9953 - val_accuracy: 0.7477 - val_loss: 1.2171 Epoch 42/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.6719 - loss: 1.0652 - val_accuracy: 0.7453 - val_loss: 1.1433 Epoch 43/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 110ms/step - accuracy: 0.7620 - loss: 0.9821 - val_accuracy: 0.8575 - val_loss: 0.8185 Epoch 44/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.7812 - loss: 0.9700 - val_accuracy: 0.8388 - val_loss: 0.8881 Epoch 45/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 123ms/step - accuracy: 0.7941 - loss: 0.8880 - val_accuracy: 0.7827 - val_loss: 0.9385 Epoch 46/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.7344 - loss: 0.9712 - val_accuracy: 0.7944 - val_loss: 0.9213 Epoch 47/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 126ms/step - accuracy: 0.7864 - loss: 0.8943 - val_accuracy: 0.5935 - val_loss: 1.9052 Epoch 48/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8281 - loss: 0.7589 - val_accuracy: 0.5958 - val_loss: 1.9160 Epoch 49/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 110ms/step - accuracy: 0.8025 - loss: 0.8794 - val_accuracy: 0.8294 - val_loss: 0.8678 Epoch 50/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8125 - loss: 0.8543 - val_accuracy: 0.8341 - val_loss: 0.8396 Epoch 51/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 126ms/step - accuracy: 0.8081 - loss: 0.8187 - val_accuracy: 0.8808 - val_loss: 0.7003 Epoch 52/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.8906 - loss: 0.6458 - val_accuracy: 0.8692 - val_loss: 0.7286 Epoch 53/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 116ms/step - accuracy: 0.8237 - loss: 0.7873 - val_accuracy: 0.6776 - val_loss: 1.3251 Epoch 54/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7500 - loss: 0.9509 - val_accuracy: 0.6729 - val_loss: 1.3382 Epoch 55/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 108ms/step - accuracy: 0.8038 - loss: 0.8187 - val_accuracy: 0.8762 - val_loss: 0.6837 Epoch 56/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7969 - loss: 0.7929 - val_accuracy: 0.8855 - val_loss: 0.6915 Epoch 57/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 123ms/step - accuracy: 0.8276 - loss: 0.7668 - val_accuracy: 0.6402 - val_loss: 1.5195 Epoch 58/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7500 - loss: 0.7971 - val_accuracy: 0.6659 - val_loss: 1.3738 Epoch 59/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 108ms/step - accuracy: 0.8176 - loss: 0.7791 - val_accuracy: 0.8879 - val_loss: 0.6344 Epoch 60/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.9062 - loss: 0.6195 - val_accuracy: 0.8855 - val_loss: 0.6310
#Observing Accuracy and Loss response for both Training and validation data
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs=60
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
Evaluating with test Data
#Calculating accuracy for test data
accuracy = model_5.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 1s - 51ms/step - accuracy: 0.8400 - loss: 0.7283
# Here we would get the output as probablities for each category
y_pred=model_5.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 1s 54ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
# Plotting the classification report
from sklearn import metrics
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.60 0.12 0.19 26
1 0.97 0.82 0.89 39
2 0.83 0.86 0.85 29
3 0.91 0.85 0.88 61
4 1.00 0.86 0.93 22
5 0.90 0.94 0.92 48
6 0.73 0.95 0.83 65
7 0.95 0.91 0.93 22
8 0.80 0.92 0.86 52
9 0.55 0.78 0.64 23
10 0.97 0.74 0.84 50
11 0.86 1.00 0.93 38
accuracy 0.84 475
macro avg 0.84 0.81 0.81 475
weighted avg 0.85 0.84 0.83 475
Observations
- This model(model_5) is the winning model which give Best performance out of all model.
Model with Transfer Learning VGG16
#Clear the keras session
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(42)
import random
random.seed(42)
tf.random.set_seed(42)
from tensorflow.keras.models import Model
from keras.applications.vgg16 import VGG16
vgg_model = VGG16(weights='imagenet', include_top = False, input_shape = (64,64,3))
vgg_model.summary()
Model: "vgg16"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None, 64, 64, 3) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block1_conv1 (Conv2D) │ (None, 64, 64, 64) │ 1,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block1_conv2 (Conv2D) │ (None, 64, 64, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block1_pool (MaxPooling2D) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block2_conv1 (Conv2D) │ (None, 32, 32, 128) │ 73,856 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block2_conv2 (Conv2D) │ (None, 32, 32, 128) │ 147,584 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block2_pool (MaxPooling2D) │ (None, 16, 16, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block3_conv1 (Conv2D) │ (None, 16, 16, 256) │ 295,168 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block3_conv2 (Conv2D) │ (None, 16, 16, 256) │ 590,080 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block3_conv3 (Conv2D) │ (None, 16, 16, 256) │ 590,080 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block3_pool (MaxPooling2D) │ (None, 8, 8, 256) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block4_conv1 (Conv2D) │ (None, 8, 8, 512) │ 1,180,160 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block4_conv2 (Conv2D) │ (None, 8, 8, 512) │ 2,359,808 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block4_conv3 (Conv2D) │ (None, 8, 8, 512) │ 2,359,808 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block4_pool (MaxPooling2D) │ (None, 4, 4, 512) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block5_conv1 (Conv2D) │ (None, 4, 4, 512) │ 2,359,808 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block5_conv2 (Conv2D) │ (None, 4, 4, 512) │ 2,359,808 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block5_conv3 (Conv2D) │ (None, 4, 4, 512) │ 2,359,808 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ block5_pool (MaxPooling2D) │ (None, 2, 2, 512) │ 0 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 14,714,688 (56.13 MB)
Trainable params: 14,714,688 (56.13 MB)
Non-trainable params: 0 (0.00 B)
# Making all the layers of the VGG model non-trainable. i.e. freezing them
for layer in vgg_model.layers:
layer.trainable = False
new_model = Sequential()
# Adding the convolutional part of the VGG16 model from above
new_model.add(vgg_model)
# Flattening the output of the VGG16 model because it is from a convolutional layer
new_model.add(Flatten())
new_model.add(Dense(128, activation='relu', kernel_regularizer='l2'))
new_model.add(Dropout(0.5))
new_model.add(Dense(32, activation='relu'))
new_model.add(Dense(12, activation='softmax'))
opt=Adam()
# Compile model
new_model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
new_model.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ vgg16 (Functional) │ (None, 2, 2, 512) │ 14,714,688 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 128) │ 262,272 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 32) │ 4,128 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_2 (Dense) │ (None, 12) │ 396 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 14,981,484 (57.15 MB)
Trainable params: 266,796 (1.02 MB)
Non-trainable params: 14,714,688 (56.13 MB)
# Epochs
epochs = 60
# Batch size
batch_size = 64
history_vgg16 = new_model.fit(train_datagen.flow(X_train_normalized,y_train_e,
batch_size=batch_size,
seed=42,
shuffle=False),
epochs=epochs,
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
validation_data=(X_val_normalized,y_val_e),
verbose=1)
Epoch 1/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 16s 172ms/step - accuracy: 0.1805 - loss: 3.8661 - val_accuracy: 0.3037 - val_loss: 2.4158 Epoch 2/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.2812 - loss: 2.4014 - val_accuracy: 0.3061 - val_loss: 2.4040 Epoch 3/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 103ms/step - accuracy: 0.2799 - loss: 2.3589 - val_accuracy: 0.3481 - val_loss: 2.1221 Epoch 4/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.2969 - loss: 2.1943 - val_accuracy: 0.3458 - val_loss: 2.1160 Epoch 5/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 123ms/step - accuracy: 0.3128 - loss: 2.1308 - val_accuracy: 0.3575 - val_loss: 2.0039 Epoch 6/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.2812 - loss: 2.1494 - val_accuracy: 0.3621 - val_loss: 2.0030 Epoch 7/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 127ms/step - accuracy: 0.3483 - loss: 2.0280 - val_accuracy: 0.3925 - val_loss: 1.9055 Epoch 8/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.2969 - loss: 2.0653 - val_accuracy: 0.3879 - val_loss: 1.8973 Epoch 9/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 108ms/step - accuracy: 0.3657 - loss: 1.9468 - val_accuracy: 0.4019 - val_loss: 1.8600 Epoch 10/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.3906 - loss: 2.0023 - val_accuracy: 0.3902 - val_loss: 1.8543 Epoch 11/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 110ms/step - accuracy: 0.3750 - loss: 1.9172 - val_accuracy: 0.4042 - val_loss: 1.8443 Epoch 12/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.5000 - loss: 1.7855 - val_accuracy: 0.4182 - val_loss: 1.8370 Epoch 13/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 119ms/step - accuracy: 0.3700 - loss: 1.8953 - val_accuracy: 0.4136 - val_loss: 1.7980 Epoch 14/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.3438 - loss: 1.8203 - val_accuracy: 0.4159 - val_loss: 1.8033 Epoch 15/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 106ms/step - accuracy: 0.3786 - loss: 1.8781 - val_accuracy: 0.4159 - val_loss: 1.8412 Epoch 16/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.4844 - loss: 1.8155 - val_accuracy: 0.4182 - val_loss: 1.8383 Epoch 17/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 124ms/step - accuracy: 0.3812 - loss: 1.8573 - val_accuracy: 0.4136 - val_loss: 1.7937 Epoch 18/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.3906 - loss: 1.8789 - val_accuracy: 0.4182 - val_loss: 1.7890 Epoch 19/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 104ms/step - accuracy: 0.3918 - loss: 1.8397 - val_accuracy: 0.4369 - val_loss: 1.7706 Epoch 20/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.4688 - loss: 1.7126 - val_accuracy: 0.4299 - val_loss: 1.7630 Epoch 21/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 105ms/step - accuracy: 0.4017 - loss: 1.7908 - val_accuracy: 0.4206 - val_loss: 1.7849 Epoch 22/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.3438 - loss: 1.8264 - val_accuracy: 0.4182 - val_loss: 1.7874 Epoch 23/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 114ms/step - accuracy: 0.4130 - loss: 1.7702 - val_accuracy: 0.4509 - val_loss: 1.7288 Epoch 24/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.4688 - loss: 1.6859 - val_accuracy: 0.4416 - val_loss: 1.7299 Epoch 25/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 128ms/step - accuracy: 0.4059 - loss: 1.7982 - val_accuracy: 0.4229 - val_loss: 1.7546 Epoch 26/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.5000 - loss: 1.7543 - val_accuracy: 0.4229 - val_loss: 1.7460 Epoch 27/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 116ms/step - accuracy: 0.4164 - loss: 1.7735 - val_accuracy: 0.4416 - val_loss: 1.7153 Epoch 28/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4219 - loss: 1.7790 - val_accuracy: 0.4393 - val_loss: 1.7308 Epoch 29/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 111ms/step - accuracy: 0.4119 - loss: 1.7670 - val_accuracy: 0.4603 - val_loss: 1.6935 Epoch 30/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4844 - loss: 1.6058 - val_accuracy: 0.4626 - val_loss: 1.6845 Epoch 31/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 118ms/step - accuracy: 0.4105 - loss: 1.7574 - val_accuracy: 0.4252 - val_loss: 1.7379 Epoch 32/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.5469 - loss: 1.5878 - val_accuracy: 0.4206 - val_loss: 1.7438 Epoch 33/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 106ms/step - accuracy: 0.4079 - loss: 1.8004 - val_accuracy: 0.4486 - val_loss: 1.6983 Epoch 34/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.3750 - loss: 1.8779 - val_accuracy: 0.4439 - val_loss: 1.6963 Epoch 35/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 127ms/step - accuracy: 0.4216 - loss: 1.7508 - val_accuracy: 0.4206 - val_loss: 1.7332 Epoch 36/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.3750 - loss: 1.6611 - val_accuracy: 0.4159 - val_loss: 1.7318 Epoch 37/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 122ms/step - accuracy: 0.4402 - loss: 1.7243 - val_accuracy: 0.4463 - val_loss: 1.7069 Epoch 38/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.3438 - loss: 1.9026 - val_accuracy: 0.4556 - val_loss: 1.6960 Epoch 39/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 104ms/step - accuracy: 0.4085 - loss: 1.7572 - val_accuracy: 0.4650 - val_loss: 1.6581 Epoch 40/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4219 - loss: 1.7447 - val_accuracy: 0.4579 - val_loss: 1.6589 Epoch 41/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 107ms/step - accuracy: 0.4252 - loss: 1.7453 - val_accuracy: 0.4696 - val_loss: 1.6444 Epoch 42/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4375 - loss: 1.7030 - val_accuracy: 0.4696 - val_loss: 1.6462 Epoch 43/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 126ms/step - accuracy: 0.4251 - loss: 1.7569 - val_accuracy: 0.4696 - val_loss: 1.6307 Epoch 44/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.3281 - loss: 1.8979 - val_accuracy: 0.4743 - val_loss: 1.6286 Epoch 45/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 102ms/step - accuracy: 0.4181 - loss: 1.7388 - val_accuracy: 0.4463 - val_loss: 1.6691 Epoch 46/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4531 - loss: 1.7011 - val_accuracy: 0.4603 - val_loss: 1.6578 Epoch 47/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 10s 108ms/step - accuracy: 0.4429 - loss: 1.7109 - val_accuracy: 0.4743 - val_loss: 1.6443 Epoch 48/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4219 - loss: 1.9026 - val_accuracy: 0.4696 - val_loss: 1.6429 Epoch 49/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 124ms/step - accuracy: 0.4229 - loss: 1.7242 - val_accuracy: 0.4486 - val_loss: 1.6382 Epoch 50/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4375 - loss: 1.6667 - val_accuracy: 0.4486 - val_loss: 1.6352 Epoch 51/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 6s 102ms/step - accuracy: 0.4302 - loss: 1.7006 - val_accuracy: 0.4416 - val_loss: 1.6696 Epoch 52/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.4375 - loss: 1.8815 - val_accuracy: 0.4276 - val_loss: 1.6765 Epoch 53/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 11s 121ms/step - accuracy: 0.4299 - loss: 1.7311 - val_accuracy: 0.4626 - val_loss: 1.6385 Epoch 54/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.3594 - loss: 1.6939 - val_accuracy: 0.4743 - val_loss: 1.6226 Epoch 55/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 7s 116ms/step - accuracy: 0.4239 - loss: 1.7246 - val_accuracy: 0.4743 - val_loss: 1.6300 Epoch 56/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.4062 - loss: 1.8453 - val_accuracy: 0.4650 - val_loss: 1.6388 Epoch 57/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 9s 105ms/step - accuracy: 0.4269 - loss: 1.7471 - val_accuracy: 0.4533 - val_loss: 1.6433 Epoch 58/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4375 - loss: 1.7012 - val_accuracy: 0.4696 - val_loss: 1.6377 Epoch 59/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 8s 131ms/step - accuracy: 0.4283 - loss: 1.7506 - val_accuracy: 0.4556 - val_loss: 1.6386 Epoch 60/60 60/60 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.4219 - loss: 1.7185 - val_accuracy: 0.4696 - val_loss: 1.6395
#Observing Accuracy and Loss response for both Training and validation data
acc = history_vgg16.history['accuracy']
val_acc = history_vgg16.history['val_accuracy']
loss = history_vgg16.history['loss']
val_loss =history_vgg16.history['val_loss']
epochs=60
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
#Calculating accuracy for test data
accuracy = new_model.evaluate(X_test_normalized, y_test_e, verbose=2)
15/15 - 3s - 174ms/step - accuracy: 0.4821 - loss: 1.6276
# Here we would get the output as probablities for each category
y_pred=new_model.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 2s 103ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_e,axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
confusion_matrix,
xticklabels=categories,
yticklabels=categories,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
plt.show()
# Plotting the classification report
from sklearn import metrics
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.40 0.23 0.29 26
1 0.39 0.36 0.37 39
2 0.58 0.24 0.34 29
3 0.62 0.67 0.65 61
4 0.00 0.00 0.00 22
5 0.46 0.12 0.20 48
6 0.53 0.95 0.68 65
7 0.50 0.50 0.50 22
8 0.62 0.50 0.55 52
9 0.00 0.00 0.00 23
10 0.56 0.56 0.56 50
11 0.28 0.74 0.40 38
accuracy 0.48 475
macro avg 0.41 0.41 0.38 475
weighted avg 0.46 0.48 0.44 475
Observations
- With transfer model VGG16, I didnt get better performance.
- May be due to VGG16 was not trained on plant seedling data.
Final Model¶
Comment on the final model you have selected and use the same in the below code to visualize the image.
#Comparing model performance
Result_df=pd.DataFrame({'Models':['Base CNN Model With SGD','Base CNN Model With Adam','Model 3 with Batch Normalization and Dropout Layer','CNN Model with Data Augmentation with 30 Epoch','CNN Model with Data Augmentation With 60 Epoch','Transfer Learning With VGG16'],'Train Accuracy':['95%','96%','84%','62%','90%','42%'],'Validation Accuracy':['75%','73%','45%','58%','88%','46%'],'Test Accuracy':['75%','72%','46.5%','57%','84%','48%']})
Result_df
| Models | Train Accuracy | Validation Accuracy | Test Accuracy | |
|---|---|---|---|---|
| 0 | Base CNN Model With SGD | 95% | 75% | 75% |
| 1 | Base CNN Model With Adam | 96% | 73% | 72% |
| 2 | Model 3 with Batch Normalization and Dropout L... | 84% | 45% | 46.5% |
| 3 | CNN Model with Data Augmentation with 30 Epoch | 62% | 58% | 57% |
| 4 | CNN Model with Data Augmentation With 60 Epoch | 90% | 88% | 84% |
| 5 | Transfer Learning With VGG16 | 42% | 46% | 48% |
Observations
- From the above Result_df,we can see that model_1,model_2,model_3 are overfitting.
- After applying data augmentation overfitting issue got solved but performance of not great.
- So that I tried to increase the number of epoch from 30 to 60 which has given amazing performance.
- May be we can use transfer learning and observe the performance, but lack of resouces I used model_5 as
Visualizing the prediction¶
# Visualizing the predicted and correct label of images from test data
plt.figure(figsize=(2,2))
plt.imshow(X_test[2])
plt.show()
print('Predicted Label', lb.inverse_transform(model_5.predict((X_test_normalized[2].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', lb.inverse_transform(y_test_e)[2]) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2,2))
plt.imshow(X_test[33])
plt.show()
print('Predicted Label', lb.inverse_transform(model_5.predict((X_test_normalized[33].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', lb.inverse_transform(y_test_e)[33]) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2,2))
plt.imshow(X_test[36])
plt.show()
print('Predicted Label', lb.inverse_transform(model_5.predict((X_test_normalized[36].reshape(1,64,64,3))))) # reshaping the input image as we are only trying to predict using a single image
print('True Label', lb.inverse_transform(y_test_e)[36])
1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 1s/step Predicted Label ['Small-flowered Cranesbill'] True Label Small-flowered Cranesbill
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step Predicted Label ['Cleavers'] True Label Cleavers
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 43ms/step Predicted Label ['Shepherds Purse'] True Label Shepherds Purse
Observations
- I have tested 3 test data images which is giving me 100% accuracy.
Actionable Insights and Business Recommendations¶
- I have seen that this dataset has imbance in distribution of target variable.If we will add more samples of data,we can improve the performance of model.
- I used data augmentation to make data balance,which is mitigating the problem of overfitting but to improve it's perfromance we can also use more different combinations of filters,adding number of convolution layer,etc.
- With this model,we can create app and distribute with farmers to quickly identify an unknown plant from a photograph, allowing them to make decisions immediately.
- This will reduce the manual work people need to put in to check if plants are growing correctly whether it is weed or good plant which will help farmer to treat weed at it's initial stage.
- As a result,better crop yields.Also,field of Agriculte educate
!jupyter nbconvert --to html CV_Project_Plant_Seedlings_Classification_by_Kirti_Kamerkar.ipynb
[NbConvertApp] Converting notebook CV_Project_Plant_Seedlings_Classification_by_Kirti_Kamerkar.ipynb to html [NbConvertApp] WARNING | Alternative text is missing on 20 image(s). [NbConvertApp] Writing 6049249 bytes to CV_Project_Plant_Seedlings_Classification_by_Kirti_Kamerkar.html